DEFAULTIMAGESRC = "./src/urmom.gif"

$(document).ready(main);
function main()
{
    CTHotkeys = new CTHotkeys()
    MyTagButtonBar = new MyTagButtonBar("#tagbuttons")
    MyTagInputForm = new MyTagInputForm("#newtag")
    MyImageBox     = new MyImageBox("#mainImage");
    MyTagList      = new MyTagList("#clicktags");

}
/////
function CTHotkeys()
{
/// Chantag's simple interface to the jquery.hotkeys plugin
//  it provides a mechanism for assigning hotkeys to newly created elements,
//  that ensures one key has no more than one associated event

    var usedKeys = {}  // e.g. {"h":"#urmom"}
    this.getUsedKeys = function(){return usedKeys;}
    this.register = function(elementID,kDnCallback,kUpCallback,keyboardKey)
    {
        if (this.getKey(elementID) )
        {
            return false;
        }
        var i = 0,tryKey;
        do
        {
            tryKey = elementID[i];
            if(i==0){tryKey = typeof(keyboardKey)!='undefined'?keyboardKey:elementID[1];}
            if(typeof(tryKey) == 'undefined'  && i>0){break;}
            i++;
        }
        while(typeof(usedKeys[tryKey]) != 'undefined');
        if(typeof(tryKey) == 'undefined'){return false;}

        usedKeys[tryKey] = elementID;

        $(document).bind('keydown',tryKey,kDnCallback);
        $(document).bind('keyup',tryKey,kUpCallback);
        return tryKey;
    }
    
    this.unRegister = function(elementID)
    {
        k = this.getKey(elementID)
        $(document).unbind('keydown',k);
        $(document).unbind('keyup',k);
    }
    this.getKey = function(elementID)
    {
        for(var key in usedKeys)
        {
            if(usedKeys[key] == elementID)
            {
                return key;
            }
        }
        return false;
    }

}

/////only the first argument is required.
function CTButton(idarg,containeridarg,actionarg,tagnamearg,hkey)
/// A general button class, with methods to create a button in a container
//
//
{

    var id          = idarg[0]=="#"? idarg:"#"+idarg;

    var tagName     = typeof(tagnamearg) != 'undefined'? tagnamearg:id.substr(1);
    var containerid = typeof(containeridarg) !='undefined'? containeridarg:"body";
    var action      = actionarg

    this.getId = function(){
        return id;
    }
    this.getHotKey = function(){
        return hotKey;
    }
    
     
    $(containerid).append('<button id="'+id.substr(1)+'">'+tagName+'</button>')
    $(id).button().click(action)
    //Makes sure that the button changes color to look clicked when its hotkey is pressed
    var kDnCallback = function(){
        if($(id).attr("aria-disabled") == "false"){
        
        $(id).addClass("ui-state-active")  
        $(id).click()
        }
    }

    var kUpCallback = function(){
        $(id).removeClass("ui-state-active")
    }

    var hotKey = CTHotkeys.register(id,kDnCallback,kUpCallback,hkey);
    if      (hotKey == "left") {hotKey = "<-" ;}
    else if (hotKey == "right"){hotKey = "->" ;}
    $(id).find("span").append(' ['+hotKey+']');

    return this
}
eye = 0;
/////
function MyTagButtonBar(idarg)
///
//
//
{
    var id       = idarg // "#tagbuttons"
    var buttons  = {};
    //Create a "next" and a "previous" button
    buttons["Prev"] = new CTButton("#Prev",id,function(){MyImageBox.setImage(CTImages.prevImage());},"Prev","left");
    buttons["Next"] = new CTButton("#Next",id,function(){MyImageBox.setImage(CTImages.nextImage());},"Next","right"); 
    this.disable = function(){
    $(id).children().attr('aria-disabled','true');
    }
    this.enable = function(){
    $(id).children().attr('aria-disabled','false');
    }

    this.addButton = function(tagname)
    {   
        if(buttons[tagname]){return false;}
        
        var action = function(){
        MyImageBox.addTagToCurrent(tagname)
        
        //setImage is called here to refresh the list of tags-- it is not strictly necessary, as getImage returns a reference,
        //not a local copy.

        }
    
        buttons[tagname] = new CTButton(tagname,id,action);
    }

}

/////
function MyTagInputForm(idarg)
///   All manipulation of, and access to the
//   form field used to input new tag names
//   should be done here.
{
    var id = idarg //"#newtag"; 
    var focus = false;

    this.setFocus = function(bool){
        if(bool)
        {   
            $(id).focus();
            MyTagButtonBar.disable()
        }
        else
        {
            $(id).blur();
            MyTagButtonBar.enable();
        }
    
        focus=bool;
    }
    this.getFocus = function(){
        return focus;
    }
    
    var onSubmit = function(){
        var tagname = $(id).val();
        if(tagname !='')
        {MyTagButtonBar.addButton(tagname);}
        setFocus(false);
        $(id).val('')
    }

    $(id).parent().submit(function(){onSubmit(); return false;})
    var setFocus = this.setFocus;
    var hotKey = CTHotkeys.register(id,function(){setFocus(true); return false;},function(){},"space");

}


function MyImageBox(idarg)
{
   var id = idarg //"#mainImage";
    var tagbarid = "#tagset";
    var num = 1;
    var currentImage;
    var getTags   = function (){return Tags;}
    var setTags   = function (tags){Tags = tags;}
    this.setImage  = function(image) {
        if(image==false || image ==undefined){return false;}
        if(image){currentImage = image;}

        $(id).attr("src",currentImage.filename); 
        $(tagbarid).text('|');

        for(i in currentImage.tags){

            if(currentImage.tags[i][0] == "-")
                {continue;}

            taghtml = "<span tagname="+currentImage.tags[i]+">"+currentImage.tags[i]+"<img src='./css/images/ecks.gif' width=10 height=10></span>|";
            $(tagbarid).append(taghtml);

        }

        $(tagbarid).find("span").click(
            function(){MyImageBox.delTagFromCurrent($(this).attr("tagname"))
        
        })
        var linkstr = image.filename.replace("/src/","/src/big/")   
            $(id).parent().attr("href",linkstr) 
            num = image.num + 1
            $("#count").text(num);
    }

    this.addTagToCurrent = function(tag){
        CTImages.addTagToImage(currentImage,tag)
        this.setImage(currentImage);
    
    }
    this.delTagFromCurrent = function (tag){
        CTImages.delTagFromImage(currentImage,tag)
        $("[tagname="+tag+"]").remove();
    }

   //When new image metadata has finished loading, display the first of the loaded images 
    $(this).ajaxSuccess(function(ajaxevent,xmlrequest,opts){

        if( 0 == opts.url.indexOf("./get") ){
            this.setImage(CTImages.selectImage(0))    
        }

    })

    this.getImage = function(){return currentImage;}

    $("#Save").button().click(function(){CTImages.saveChanges();MyImageBox.setImage(false);})
}


/////
CTImages = new function()
///  Attempts to preload images, and serves as an intermediary between MyImageBox and the server.
//
//
{
    this.preload = true
    var imgDB = new Object()  // used as a dictionary, where a concatenated comma delimited list of tags is the key to a list of data objects, each of which contains the path, tags, and "number",(i.e. number passed to /get in loadImageData) of the image.  You should be able to use getImage to get images out of it, without worrying about the internal representation (i.e. imgDB). 

    this.loadImages = function (min,max,tags){
        var queryData = new Object();
        queryData["min"] = min;
        queryData["max"] = max;
        for(i in tags){queryData[i]=tags[i];} 
        $.getJSON("./get",queryData,storeImageData)
    }

    var storeImageData = function(data){
    
        var tagKey = data[1].sort().join()
        for (i in data[0])
        {

            if(this.preload)
            {
                var img = new Image;
                img.src = data[0][i].filename
            }
            if(!imgDB[tagKey]){imgDB[tagKey] = [];}
            enn = parseInt(data[0][i]["num"])
            imgDB[tagKey][enn] = data[0][enn];
        }
    }

        var getImage = function(num,tags){
        sortedtags = tags.sort().join()
        var img = imgDB[sortedtags][num];
        return img
    }
        var setImage = function(image){
            sortedtags = image.tags.sort().join();
            imgDB[sortedtags][image.num] = image
        }

    // Adds a tag to the image specified by Image-- note that including this function in CTImages is a little misleading-- the function will 
    // add a tag to the image passed in whether it belongs to CTImages or not.  /shrug
    this.addTagToImage = function(image,tag){
        if(typeof(tag)!="string")
            {return false;}
        for(i in image.tags){
            if(image.tags[i] == tag || image.tags[i]== "*"+tag)
                {return false;}
        }
        image.tags.push("*"+tag);
    }

    this.delTagFromImage = function(image,tag){
        tag = "-"+tag;
        tagname = tag.substr(1)
        for(var i in image.tags)
        {
            if(image.tags[i] == tagname)
                {image.tags[i] = tag}
        }

    }
 
    this.saveChanges = function() {
        var changes = {};
        // in plain English:   in every file cooresponding to every tag-grouping, check to see
        // check every tag for an asterisk.  If you see one, add the tag, minus the asterisk, to the dictionary 'changes'
        // and make its key its filename
        for (var key in imgDB){
            for (var fnum in imgDB[key]){
                for (var i in imgDB[key][fnum].tags){
                    var ourtag = imgDB[key][fnum].tags[i];
                    if(ourtag[0]== "*" || ourtag[0] =="-"){
                       // console.log("found a change!")
                        sKey = imgDB[key][fnum].filename
                        if(typeof(changes[sKey]) == 'undefined')
                            {changes[sKey] = [];}
                        changes[sKey].push(ourtag)
                        imgDB[key][fnum].tags[i] = ourtag.substr(1);
                    }
                    if(ourtag[0] == "-"){imgDB[key][fnum].tags.splice(i,1)}
                }
            }
        }

        $.getJSON("./save",escape(JSON.stringify(changes)));
        return changes
    }
///////The following functions are for convenience--
/////// the CTImages class works without them.  They are included because I need their functionality, and 
/////// can't think of a more appropriate place to put them.

    this.setCurTags = function(tags){curTags = tags;}
    var im=0;
    var curTags = ["new"];
    this.loadImages(0,-1,curTags);
    this.selectImage = function(inc,tagsarg){
       im = im + inc;
       if(!tagsarg){tagsarg=curTags;}
       tagstring = tagsarg.sort().join();
       if(curTags.sort().join() != tagstring)
        {
            curTags = tagsarg;
            //loadImages fires an AJAX event to fetch a dictionary of image metadata entries from the server.
            //until this dictionary is loaded, we can't load the first image.
            //Therefore, we rely on a callback function, specified in MyImageBox, to call selectImage again when the 
            //ajax request completes.  See MyImageBox
            this.loadImages(0,1000,tagsarg)
            tmp = 0
            im = 0;
            return false;
        }
        else{tmp =  getImage(im,tagsarg);return tmp;}
    }

    this.nextImage = function(tagsarg){
    return this.selectImage(1,tagsarg);
    }
    this.prevImage = function(tagsarg){

    return this.selectImage(-1,tagsarg);
    }

}


function MyTagList(idarg)
{
    var id = idarg //"#clicktags";
    
    //populates #clicktags with information from the server.
    $.getJSON("./stats",null,function(data){
    for (i in data)
    {
        $(id).append('<li tag="'+data[i][0]+'">['+data[i][1]+']'+data[i][0]+'</li>')
    }

    });

    //Tells jquery how to treat the li items in #clicktags 
    $(id).selectable({
         stop:    function(){
            MyTagList.refreshCurrentTags(); 
            MyImageBox.setImage(CTImages.selectImage(0,MyTagList.getCurrentTags()) );
            }
        ,selected:function(e,li){
            
            if(e.shiftKey){
                $(li.selected).addClass("ui-selected-not")
                }
        }
        ,unselected:function(e,li){$(li.unselected).removeClass("ui-selected-not")}

        });        

    var currentTags;
    this.refreshCurrentTags = function (){
        var vals =[];
        $("li.ui-selected").each(function(i,val){
            if($(val).hasClass("ui-selected-not"))
            {vals[i] ="N:"+vals[i]}
            else
            {vals[i] = $(val).attr("tag")}

        })

            currentTags = vals;
        }
    this.getCurrentTags = function(){return currentTags;}
}
